﻿//-----------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="AFS">
// Copyright (c) AFS. All rights reserved.
//
//    This file is part of Chatbox.
//
//    Chatbox is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    any later version.
//
//    Chatbox is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with Chatbox. If not, see http://www.gnu.org/copyleft/lesser.html
// </copyright>
//----------------------------------------------------------------------- 

namespace ChatBox.UI.Windows.Main
{
    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media.Animation;
    using System.Windows.Navigation;

    using ChatBox.DomainObjects.Settings;
    using ChatBox.DomainObjects.XmlFeedObjects;
    using ChatBox.Interfaces;
    
    using ChatBox.UI.Controls;
    using ChatBox.UI.MessageDisplay;
    using ChatBox.UI.ViewModels;
    using ChatBox.UI.Windows.SendPrivateMessage;
    using ChatBox.XamlUtility;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow 
    {
        #region data

        /// <summary>
        /// Storyboard to show the speak area
        /// </summary>
        private Storyboard showSpeakAnimation;

        /// <summary>
        /// Storyboard to hide the speak area
        /// </summary>
        private Storyboard hideSpeakAnimation;

        /// <summary>
        /// Storyboard to show the other users area
        /// </summary>
        private Storyboard showOtherUsersAnimation;

        /// <summary>
        /// Storyboard to hide the other users area
        /// </summary>
        private Storyboard hideOtherUsersAnimation;

        /// <summary>
        /// Storyboard to show the topic area
        /// </summary>
        private Storyboard showTopicAnimation;

        /// <summary>
        /// Storyboard to hide the topic area
        /// </summary>
        private Storyboard hideTopicAnimation;

        /// <summary>
        /// Storyboard to show the throbber
        /// </summary>
        private Storyboard showThrobberAnimation;

        /// <summary>
        /// Storyboard to hide the throbber
        /// </summary>
        private Storyboard hideThrobberAnimation;

        #endregion

        /// <summary>
        /// Initializes a new instance of the MainWindow class
        /// </summary>
        public MainWindow()
        {
            this.InitializeComponent();

            // show that log on state has been reset
            this.throbber.LogOnState = ConnectionState.NotConnected;

            this.InitializeStoryboards();
            this.InitializeViewModel();

            // can do this when the viewmodel is attached
            this.ViewModel.StatusText = "Starting Chatbox";

            this.InitializeFormatters();

            this.LoadWindowPosition();
            this.ViewModel.RefreshFontSizes(true);
            this.otherUsers.OperationCompletedCommand = this.ViewModel.OperationCompletedCommand;

            // only start the chat engine after we are getting notification of errors and data
            this.ChatEngine.StartPublic();
        }

        /// <summary>
        /// Gets the chat Engine
        /// </summary>
        public IChatEngine ChatEngine
        {
            get
            {
                if (this.ViewModel == null)
                {
                    return null;
                }

                return this.ViewModel.ChatEngine;
            }
        }

        /// <summary>
        /// Gets the window's attached view model
        /// </summary>
        public MainWindowViewModel ViewModel
        {
            get
            {
                return this.DataContext as MainWindowViewModel;
            }
        }
        
        /// <summary>
        /// Initialize the view model
        /// </summary>
        private void InitializeViewModel()
        {
            MainWindowViewModel newViewModel = Locator.Resolve<MainWindowViewModel>();
            newViewModel.MainWindowReadSettingsCommand.Execute(newViewModel);

            this.DataContext = newViewModel;
            this.ViewModel.ThemeManager.CurrentTheme = this.ViewModel.UISettingsRepository.ReadSettings().Theme;

            // hook up the view model
            this.ViewModel.Window = this;
            this.ViewModel.Throbber = this.throbber;
            this.ViewModel.OtherUsers = this.otherUsers;
            this.ViewModel.ScrollToBottom = new DocumentViewerScrollToBottom(this.messagesDocumentViewer);
            this.ViewModel.HyperlinkContext = this.MakeHyperlinkContext();
            this.ViewModel.PropertyChanged += this.ViewModelPropertyChanged;
            this.ViewModel.Speak.PropertyChanged += this.SpeakViewModelPropertyChanged;

            this.ViewModel.SendMessageButtonStyle = this.FindStyle("SendPrivateMessageButtonStyle");

            this.ViewModel.ReadEngineLogOn();
            this.showHideSpeakUpDown.IsUp = ! this.ViewModel.UISettingsRepository.ReadSettings().ShowSpeak;

            this.DataContext = this.ViewModel;
        }

        /// <summary>
        /// Initialize the formatter classes
        /// </summary>
        private void InitializeFormatters()
        {
            this.ViewModel.PublicMessageFormatOptions = this.GetPublicMessageFormatOptions();
            this.ViewModel.TopicFormatOptions = this.GetTopicFormatOptions();

            this.ViewModel.MessageFormatter = new MessageFormatter(this.ViewModel.HyperlinkContext, this.ChatEngine.Logger);
            this.ViewModel.TopicFormatter = new TopicFormatter(this.ViewModel.HyperlinkContext, this.ChatEngine.Logger);
        }

        /// <summary>
        /// Create public message format options for this window
        /// </summary>
        /// <returns>Format options for public messages</returns>
        private FormatOptions GetPublicMessageFormatOptions()
        {
            UISettingsData uiSettingsData = this.ViewModel.UISettingsRepository.ReadSettings();

            FormatOptions publicMessageFormatOptions = new FormatOptions(uiSettingsData.PublicMessageFontSize, DateTimeDisplay.TimeOnly, true);
            publicMessageFormatOptions.ShowSendPrivateMessageButton = uiSettingsData.ShowSendPrivateMessageButtonInPublicChat;
            publicMessageFormatOptions.SendPrivateMessageButtonStyle = this.ViewModel.SendMessageButtonStyle;
            publicMessageFormatOptions.SendPrivateMessageButtonHandler = this.SendPrivateMessageClick;

            return publicMessageFormatOptions;
        }

        /// <summary>
        /// Create public message format options for this window
        /// </summary>
        /// <returns>Format options for private messages</returns>
        private FormatOptions GetTopicFormatOptions()
        {
            UISettingsData uiSettingsData = this.ViewModel.UISettingsRepository.ReadSettings();
            return new FormatOptions(uiSettingsData.TopicFontSize, DateTimeDisplay.None, true);
        }

        /// <summary>
        /// Create a hyperlink context for this window
        /// </summary>
        /// <returns>the new hyperlink context</returns>
        private HyperlinkContext MakeHyperlinkContext()
        {
            ServerSettingsData serverSettingsData = this.ViewModel.ServerSettingsRepository.ReadSettings();
            return new HyperlinkContext(
               serverSettingsData.Server,
               this.LinkClick,
               this.LinkMouseEnter,
               this.LinkMouseLeave);   
        }

        /// <summary>
        /// Launch the private message send dialog
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="link">the user to send the message to</param>
        private void SendPrivateMessageClick(object sender, Link link)
        {
            ShowPrivateMessageParameters model = new ShowPrivateMessageParameters(
                this, 
                link, 
                this.ViewModel.MessageFormatter.HyperlinkContext,
                this.ViewModel.OperationCompletedCommand);
            this.ViewModel.ShowSendPrivateMessageCommand.Execute(model);
        }

        /// <summary>
        /// Set up the storyboards 
        /// </summary>
        private void InitializeStoryboards()
        {
            this.showSpeakAnimation = this.FindStoryboard("showSpeakAnimation");
            this.hideSpeakAnimation = this.FindStoryboard("hideSpeakAnimation");
            this.showOtherUsersAnimation = this.FindStoryboard("showOtherUsersAnimation");
            this.hideOtherUsersAnimation = this.FindStoryboard("hideOtherUsersAnimation");
            this.showTopicAnimation = this.FindStoryboard("showTopicAnimation");
            this.hideTopicAnimation = this.FindStoryboard("hideTopicAnimation");
            this.showThrobberAnimation = this.FindStoryboard("showThrobberAnimation");
            this.hideThrobberAnimation = this.FindStoryboard("hideThrobberAnimation");

            // Change state when the animation completes
            this.showSpeakAnimation.Completed += (sender, e) =>
            {
                this.ViewModel.Speak.SpeakVisibilityState = VisibilityAnimationState.Shown;
                this.ViewModel.WriteSettings();
            };

            this.hideSpeakAnimation.Completed += (sender, e) =>
                 {
                     this.ViewModel.Speak.SpeakVisibilityState = VisibilityAnimationState.Collapsed;
                     this.ViewModel.WriteSettings();
                 };

            this.showOtherUsersAnimation.Completed += (sender, e) =>
            {
                this.ViewModel.OtherUsersVisibilityState = VisibilityAnimationState.Shown;
                this.ViewModel.WriteSettings();
            };

            this.hideOtherUsersAnimation.Completed += (sender, e) =>
                 {
                     this.ViewModel.OtherUsersVisibilityState = VisibilityAnimationState.Collapsed;
                     this.ViewModel.WriteSettings();
                 };

            this.showTopicAnimation.Completed += (sender, e) =>
                {
                    this.ViewModel.TopicVisibilityState = VisibilityAnimationState.Shown;
                    this.ViewModel.WriteSettings();
                };

            this.hideTopicAnimation.Completed += (sender, e) =>
                {
                    this.ViewModel.TopicVisibilityState = VisibilityAnimationState.Collapsed;
                    this.ViewModel.WriteSettings();
                };

            this.showThrobberAnimation.Completed += (sender, e) =>
            {
                this.ViewModel.ThrobberVisibilityState = VisibilityAnimationState.Shown;
                this.ViewModel.WriteSettings();
            };

            this.hideThrobberAnimation.Completed += (sender, e) =>
            {
                this.ViewModel.ThrobberVisibilityState = VisibilityAnimationState.Collapsed;
                this.ViewModel.WriteSettings();
            };
        }

        /// <summary>
        /// View Model property changed event handler
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event params</param>
        private void ViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "TopicVisibilityState":
                {
                    if (this.ViewModel.TopicVisibilityState == VisibilityAnimationState.Showing)
                    {
                        this.showTopicAnimation.Begin();
                    }
                    else if (this.ViewModel.TopicVisibilityState == VisibilityAnimationState.Hiding)
                    {
                        this.hideTopicAnimation.Begin();
                    }

                    break;
                }

                case "ThrobberVisibilityState":
                {
                    if (this.ViewModel.ThrobberVisibilityState == VisibilityAnimationState.Showing)
                    {
                        this.showThrobberAnimation.Begin();
                    }
                    else if (this.ViewModel.ThrobberVisibilityState == VisibilityAnimationState.Hiding)
                    {
                        this.hideThrobberAnimation.Begin();
                    }

                    break;
                }

                case "OtherUsersVisibilityState":
                {
                    if (this.ViewModel.OtherUsersVisibilityState == VisibilityAnimationState.Showing)
                    {
                        this.showOtherUsersAnimation.Begin();

                        if (this.ChatEngine.OtherUsersSource != null)
                        {
                            this.ChatEngine.OtherUsersSource.Start();
                        }
                    }
                    else if (this.ViewModel.OtherUsersVisibilityState == VisibilityAnimationState.Hiding)
                    {
                        this.hideOtherUsersAnimation.Begin();
                    }

                    break;
                }
            }
        }

        /// <summary>
        /// Speak View Model property changed event handler
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event params</param>
        private void SpeakViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "SpeakVisibilityState")
            {
                if (this.ViewModel.Speak.SpeakVisibilityState == VisibilityAnimationState.Showing)
                {
                    this.showSpeakAnimation.Begin();
                    this.showHideSpeakUpDown.IsUp = false;
                }
                else if (this.ViewModel.Speak.SpeakVisibilityState == VisibilityAnimationState.Hiding)
                {
                    this.hideSpeakAnimation.Begin();
                    this.showHideSpeakUpDown.IsUp = true;
                }
            }
        }

        /// <summary>
        /// Handler for when a link was clicked
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event args</param>
        private void LinkClick(object sender, RequestNavigateEventArgs e)
        {
            this.ViewModel.NavigateToUri(e.Uri);
            e.Handled = true;
        }
        
        /// <summary>
        /// Handler for mouse over a link
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event args</param>
        private void LinkMouseEnter(object sender, MouseEventArgs e)
        {
            this.ViewModel.LinkClick(sender as Hyperlink);
        }

        /// <summary>
        /// Handler for mouse no longer over a link
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event args</param>
        private void LinkMouseLeave(object sender, MouseEventArgs e)
        {
            this.ViewModel.StatusLinkText = string.Empty;
        }

        /// <summary>
        /// Window closed event handler
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event params</param>
        private void Window_Closed(object sender, EventArgs e)
        {
            this.ViewModel.ShutdownCommand.Execute(this.ViewModel);
        }

        /// <summary>
        /// Load the window size and position
        /// </summary>
        private void LoadWindowPosition()
        {
            UISettingsData settingsData = this.ViewModel.UISettingsRepository.ReadSettings();
            this.SetWindowPosition(settingsData.MainWindowDimensions);
        }

        /// <summary>
        /// Key pressed in the speak box
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event params</param>
        private void MessageText_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Return)
            {
                this.ViewModel.SpeakCommand.Execute(this.ViewModel);
            }
        }

        #region menu and toolbar event handlers

        /// <summary>
        /// Exit menu item selected
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event params</param>
        private void MenuExitClick(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Click on the "expand speak" button
        /// </summary>
        /// <param name="sender">the message sender</param>
        /// <param name="e">the message params</param>
        private void SpeakUpClick(object sender, EventArgs e)
        {
            this.ViewModel.Speak.IsSpeakVisible = true;
        }

        /// <summary>
        /// Click on the "collapse speak" button
        /// </summary>
        /// <param name="sender">the message sender</param>
        /// <param name="e">the message params</param>
        private void SpeakDownClick(object sender, EventArgs e)
        {
            this.ViewModel.Speak.IsSpeakVisible = false;
        }

        #endregion
    }
}
